﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ERF;
using Microsoft.Xna.Framework;
using ERF.Physics;
using FirstPerson.States;

namespace FirstPerson.Objects.Weapons
{
    /// <summary>
    /// Represents a weapon that uses rays for collision checking.
    /// </summary>
    abstract class RayWeapon : WeaponBase
    {
        private float weaponRange;

        /// <summary>
        /// Creates a new instance of RayWeapon.
        /// </summary>
        /// <param name="game">The game this instance belongs to.</param>
        /// <param name="holder">The player that is the holder of this weapon.</param>
        /// <param name="weaponModel">The asset name for the model that this weapon uses.</param>
        /// <param name="positionOffset">The position offset of this weapon, relative to the player.</param>
        /// <param name="weaponRange">The distance, in meters, that this weapon can fire.</param>
        /// <param name="recoilFactor">The recoil factor determining how much recoil the weapon has.</param>
        public RayWeapon(ERFGame game, Player holder, string weaponModel, Vector3 positionOffset, float weaponRange)
            : base(game, holder, weaponModel, positionOffset)
        {
            this.weaponRange = weaponRange;
        }

        /// <summary>
        /// Emitts a ray and performs collision checks on it to see if anything was hit.
        /// </summary>
        public override void Shoot()
        {
            Ray ray;
            // If the holder is a human player, we need to get a ray from the mouse cursor.
            // If the holder isn't a human player, we simply construct a ray from the forward vector and eyeposition of the player.
            if (this.holder is HumanPlayer)
            {
                this.GetCursorRay(out ray);
            }
            else
            {
                Vector3 startPosition = this.holder.EyePosition;

                Vector3 direction = this.ForwardVector;
                direction.Normalize();
                ray = new Ray(startPosition, direction);
            }

            PhysicsEngine engine = PlayingState.GetInstance(base.CurrentGame.StateMachine).PhysicsEngine;

            Body body;
            Vector3? terrainPosition;
            PhysicsEngine.RayIntersectionType intersection = engine.RayIntersection(ref ray, base.holder.PhysicsBody, this.weaponRange, out body, out terrainPosition);

            // If we hit the terrain, apply a decal (Experimental!), and add some smoke.
            if (intersection == PhysicsEngine.RayIntersectionType.Terrain)
            {
                Vector3 position = (Vector3)terrainPosition;
                Vector3 normal = engine.Scene.GetNormalAtXZ(position.X, position.Z);

                Decal decal = new Decal(base.CurrentGame, position, normal, "Textures\\bullethole2");
                base.CurrentGame.CurrentSceneGraph.Items.Add(decal);
                for(int i = 0; i < 5; i++)
                    PlayingState.GetInstance(this.CurrentGame.StateMachine).particleSystem.AddParticle((Vector3)terrainPosition, Vector3.Zero);
            }
            else if (intersection == PhysicsEngine.RayIntersectionType.Body) 
            {
                if (body.Owner is Player)
                {
                    base.HitPlayer(body.Owner as Player);
                } 
            }
        }

        /// <summary>
        /// Creates a ray in the direction of the cursor.
        /// </summary>
        /// <param name="ray">Will contain a new Ray when this method call returns.</param>
        private void GetCursorRay(out Ray ray)
        {
            // The crosshair is always in the center of the screen.
            Vector2 crossHairPosition = new Vector2(base.CurrentGame.GraphicsDevice.Viewport.Width / 2, base.CurrentGame.GraphicsDevice.Viewport.Height / 2);

            // Find the near 3D coordinate based on the crosshair position.
            Vector3 nearPosition = base.CurrentGame.GraphicsDevice.Viewport.Unproject(
                new Vector3(crossHairPosition.X, crossHairPosition.Y, 0),
                base.CurrentGame.Camera.Projection,
                base.CurrentGame.Camera.View,
                Matrix.Identity);

            // Find the far 3D coordinate based on the crosshair position.
            Vector3 farPosition = base.CurrentGame.GraphicsDevice.Viewport.Unproject(
                new Vector3(crossHairPosition.X, crossHairPosition.Y, 10),
                base.CurrentGame.Camera.Projection,
                base.CurrentGame.Camera.View,
                Matrix.Identity);

            // Subtract to find direction.
            Vector3 direction = nearPosition - farPosition;
            direction.Normalize();

            ray = new Ray(nearPosition, direction);
        }

        public override Vector3 ForwardVector
        {
            get
            {
                return this.world.Backward;
            }
        }
    }
}
